/* tcpread.c - tcpread */

#include "tcpfms.h"
#include "tcb.h"

int tcpgetdata(struct tcb *, u_char *, unsigned);

/*------------------------------------------------------------------------
 *  tcpread  -  read one buffer from a TCP pseudo-device
 *------------------------------------------------------------------------
 */
int tcpread(int tcbnum, char *pch, unsigned len)
{
    if(tcbnum <= 0 || tcbnum > NTCP)
        return SYSERR;

    struct	tcb	*ptcb = tcbtab[tcbnum];
    int		state = ptcb->tcb_state;
    int		cc;

    if (state != TCPS_ESTABLISHED && state != TCPS_CLOSEWAIT)
        return SYSERR;
retry:
    sem_wait(&(ptcb->tcb_rsema));
    sem_wait(&(ptcb->tcb_mutex));

    if (ptcb->tcb_state == TCPS_FREE)
        return SYSERR;			/* gone		*/
    if (ptcb->tcb_error)
    {
        tcpwakeup(READERS, ptcb);	/* propagate it */
        sem_post(&(ptcb->tcb_mutex));
        return ptcb->tcb_error;
    }
    if (ptcb->tcb_flags & TCBF_RUPOK) {
        cc = tcpgetdata(ptcb, (u_char*)pch, len);
    }
    else
    {
        if (len > ptcb->tcb_rbcount &&
            ptcb->tcb_flags & TCBF_BUFFER &&
            (ptcb->tcb_flags & (TCBF_PUSH|TCBF_RDONE)) == 0)
        {
            sem_post(&(ptcb->tcb_mutex));
            goto retry;
        }
        else
            cc = tcpgetdata(ptcb, (u_char*) pch, len);
    }
    tcpwakeup(READERS, ptcb);
    sem_post(&(ptcb->tcb_mutex));
    return cc;
}
